"""
       @File     : pre1.py
       @IDE      : PyCharm
       @Author   : 陈引弟
       @Date     : 2025/3/9 14:43
       @Desc     :
=========================================================
"""


def predict_weather():
    # 原有导入和数据处理代码...
    # 导入Python的数据处理库pandas，相当于python里的exceL
    import pandas as pd

    # 导入python绘图imatplotLib
    import matplotlib
    matplotlib.use('Agg')  # 在导入 pyplot 之前设置后端
    import matplotlib.pyplot as plt
    # 设置绘图大小
    plt.style.use({'figure.figsize': (25, 20)})
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

    # 调用pandas的read_csV函数读取数据集文件
    df = pd.read_csv(r'F:\python\基于大数据的天气预测分析系统\model\weather.csv')
    # 重新调用pandas的read_csV函数读取数据集文件
    df2 = pd.read_csv(r'F:\python\基于大数据的天气预测分析系统\model\weather.csv')

    df2.head()
    df2['Date'] = pd.to_datetime(df2['Date'])
    # 构造新的一列:年
    df2.loc[:, 'year'] = df2['Date'].apply(lambda x: x.year)
    # 构造新的一列:月
    df2.loc[:, 'month'] = df2['Date'].apply(lambda x: x.month)
    # 构诰新的一列，是期几 #构造新的一列:星期几
    df2.loc[:, 'dow'] = df2['Date'].apply(lambda x: x.dayofweek)
    # 构造新的一列:一个月第几天
    df2.loc[:, 'dom'] = df2['Date'].apply(lambda x: x.day)
    # 构造新的三列、是不是周末、是不是周六、是不是周日
    df2.loc[:, 'weekend'] = df2['Date'].apply(lambda x: x.dayofweek > 4)
    df2.loc[:, 'weekend_sat'] = df2['Date'].apply(lambda x: x.dayofweek == 5)
    df2.loc[:, 'weekend_sun'] = df2['Date'].apply(lambda x: x.dayofweek == 6)

    df2.head()

    # 添加上半月和下半月的信息
    def half_month(day):
        if day in range(1, 16):
            return 1
        else:
            return 2

    df2.loc[:, 'half_month'] = df2['dom'].apply(lambda x: half_month(x))

    # 添加每个月上中下旬的信息
    def three_part_month(day):
        if day in range(1, 11):
            return 1
        if day in range(11, 21):
            return 2
        else:
            return 3

    df2.loc[:, 'three_part_month'] = df2['dom'].apply(lambda x: three_part_month(x))

    # 添加每个月四下层期的信息
    def four_week_month(day):
        if day in range(1, 8):
            return 1
        if day in range(8, 15):
            return 2
        if day in range(15, 22):
            return 3
        else:
            return 4

    df2.loc[:, 'four_week_month'] = df2['dom'].apply(lambda x: four_week_month(x))

    df2.head(35)

    # 派加放假注信息
    df2.loc[:, 'festival'] = 0
    df2.loc[(df2.month == 1) & (df2.dom < 4), 'festival'] = 1

    before_dummy_df = df2.copy()

    # 构造叫放据集的特征
    drop_columns = ['Date', 'Temp']
    X_before_dummy = before_dummy_df.drop(drop_columns, axis=1)
    X_before_dummy.head()

    # 没置要进行独热向量编码的列
    columns_to_encoding = ['year', 'month', 'dow', 'dom', 'three_part_month', 'four_week_month']

    # 使田pandas点get_net
    dummy_df = pd.get_dummies(df2, columns=columns_to_encoding)

    Y = dummy_df['Temp']
    drop_columns = ['Date', 'Temp']
    X_dummy = dummy_df.drop(drop_columns, axis=1)

    from sklearn.model_selection import train_test_split
    x_train, x_test, y_train, y_test = train_test_split(X_dummy, Y, test_size=0.2, random_state=1, shuffle=True)

    from sklearn import linear_model
    from sklearn.neighbors import KNeighborsRegressor

    lr_reg = linear_model.LinearRegression()
    lr_reg.fit(x_train, y_train)
    print('截距', lr_reg.intercept_)
    print('斜率（线性模型中各特征对应的系数）', lr_reg.coef_)

    df.loc[:, '线性回归'] = lr_reg.predict(X_dummy)

    knn_reg = KNeighborsRegressor()
    knn_reg.fit(x_train, y_train)
    df.loc[:, 'KNN'] = knn_reg.predict(X_dummy)

    from sklearn.preprocessing import PolynomialFeatures

    # 构建一个特征处理器
    poly_reg = PolynomialFeatures(degree=2)

    # 使用构建的·二次多项式处理特征
    X_poly = poly_reg.fit_transform(X_dummy)

    from sklearn import linear_model
    lin_reg_2 = linear_model.LinearRegression()
    lin_reg_2.fit(X_poly, Y)

    df.loc[:, '二次多项式回归'] = lin_reg_2.predict(X_poly)

    from sklearn.model_selection import train_test_split
    x_train, x_test, y_train, y_test = train_test_split(X_before_dummy, Y, test_size=0.2, random_state=1, shuffle=True)

    # 从python 机器学习与数据挖掘工具库sklearn中导入随机森林回归器
    from sklearn.ensemble import RandomForestRegressor

    #
    #
    from sklearn.model_selection import GridSearchCV

    #
    param_grid = {
        'n_estimators': [5, 10, 20, 50, 100, 200],  # 决策树的个数
        'max_depth': [3, 5, 7],
        'max_features': [0.6, 0.7, 0.8, 1]
    }

    # 实例化随机森林回归器
    rf = RandomForestRegressor()

    #
    grid = GridSearchCV(rf, param_grid=param_grid, cv=3)

    # 在训练集上训练
    grid.fit(x_train, y_train)

    # 查看效果最好的参数
    print(grid.best_params_)

    #
    rf_reg = grid.best_estimator_

    import numpy as np
    print('特征排序：')
    feature_names = ['year', 'month', 'dow', 'dom', 'weekend', 'weekend_sat', 'weekend_sun',
                     'half_month', 'three_part_month', 'four_week_month', 'festival']
    feature_importances = rf_reg.feature_importances_
    indices = np.argsort(feature_importances)[::-1]

    for index in indices:
        print("feature %s (%f)" % (feature_names[index], feature_importances[index]))

    plt.figure(figsize=(16, 8))
    plt.title("随机森林模型不同特征的重要度")
    plt.bar(range(len(feature_importances)), feature_importances[indices], color='b')
    plt.xticks(range(len(feature_importances)), np.array(feature_names)[indices], color='b')

    df.loc[:, '随机森林'] = rf_reg.predict(X_before_dummy)

    #
    fig, ax = plt.subplots(figsize=(30, 25))

    # 绘制气温散点图
    df.plot(x='Date', y='Temp', style='k', ax=ax, label='Temp')

    # 绘制二次多项式

    # knn
    ax.plot(df['Date'], df['KNN'], 'g.', label='KNN')

    ax.plot(df['Date'], df['随机森林'], 'r.', label='随机森林')

    #
    ax.plot(df['Date'], df['线性回归'], 'b.', label='线性回归')

    plt.legend(fontsize=20, markerscale=5)
    # 设置坐标文字大小
    plt.tick_params(labelsize=25)
    #
    plt.grid()
    #
    plt.show()

    df.to_csv('final_regression.csv', index=False)

    result = pd.read_csv('final_regression.csv')

    result.head()

    result_analyse = result.describe().copy()

    result_analyse

    # 绘制
    result_analyse.loc['MSE', :] = 0

    result_analyse

    # 构造计算均方误差的函数
    def MSE(yhat, y):
        error = np.array(yhat - y)
        error_power = np.power(error, 2)
        MSE_error = np.sum(error_power) / len(y)
        return MSE_error

    #
    for each in result_analyse.columns:
        result_analyse.loc['MSE', each] = MSE(result[each], result['Temp'])

    result_analyse

    plt.figure(figsize=(20, 20))
    plt.subplot(421)
    plt.title("平均值")
    result_analyse.loc['mean', :].plot(kind="bar", color='k')
    plt.subplot(422)
    plt.title('方差')
    result_analyse.loc['std':].plot(kind='bar', color='y')
    plt.subplot(423)
    plt.title('最小值')
    result_analyse.loc['min', :].plot(kind="bar", color='m')
    plt.subplot(424)
    plt.title('下四分位数')
    result_analyse.loc['25%', :].plot(kind='bar', color='c')
    plt.subplot(425)
    plt.title('中位数')
    result_analyse.loc['50%', :].plot(kind='bar', color='r')
    plt.subplot(425)
    plt.title('上四分位数')
    result_analyse.loc['75%', :].plot(kind='bar', color='g')
    plt.subplot(427)
    plt.title('最大值')
    result_analyse.loc['max', :].plot(kind='bar', color='b')
    plt.subplot(423)
    plt.title("均方误差")
    result_analyse.loc['MSE':].plot(kind='bar', color='deepskyblue')
    plt.subplots_adjust(wspace=0.07, hspace=0.6)  # 训整子图间距
    # plt.show()

    import numpy as np
    import pandas as pd
    from datetime import timedelta, datetime

    # 获取数据集最后一天的日期
    last_date = df2['Date'].max()
    # 获取本地电脑当前时间
    # last_date=datetime.now()
    # 生成未来7天的日期
    future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=7)

    #
    future_df = pd.DataFrame({'Date': future_dates})

    #
    future_df['year'] = future_df['Date'].apply(lambda x: x.year)
    future_df['month'] = future_df['Date'].apply(lambda x: x.month)
    future_df['dow'] = future_df['Date'].apply(lambda x: x.dayofweek)
    future_df['dom'] = future_df['Date'].apply(lambda x: x.day)

    #
    # 添加周末、上中下旬、四周等信息ر
    future_df['weekend'] = future_df['Date'].apply(lambda x: x.dayofweek > 4)
    future_df['weekend_sat'] = future_df['Date'].apply(lambda x: x.dayofweek == 5)
    future_df['weekend_sun'] = future_df['Date'].apply(lambda x: x.dayofweek == 6)
    future_df['half_month'] = future_df['dom'].apply(lambda x: 1 if x in range(1, 16) else 2)
    future_df['three_part_month'] = future_df['dom'].apply(
        lambda x: 1 if x in range(1, 11) else (2 if x in range(11, 21) else 3))
    future_df['four_week_month'] = future_df['dom'].apply(
        lambda x: 1 if x in range(1, 8) else (2 if x in range(8, 15) else (3 if x in range(15, 22) else 4)))
    future_df['festival'] = 0

    # 处理独热编码
    columns_to_encoding = ['year', 'month', 'dow', 'dom', 'three_part_month', 'four_week_month']
    future_df_encoded = pd.get_dummies(future_df, columns=columns_to_encoding)

    # 确保未来数据与模型训练的数据结构一致
    missing_cols = set(X_dummy.columns) - set(future_df_encoded.columns)
    for col in missing_cols:
        future_df_encoded[col] = 0

    future_df_encoded = future_df_encoded[X_dummy.columns]
    # 便用二次多项式特征处理器构造未来7天的二次特征
    X_future_poly = poly_reg.transform(future_df_encoded)
    # 使用训练好的二次多项式回归模型进行预测
    future_predictions = lin_reg_2.predict(X_future_poly)

    # 将预测结果和未来日期关联
    future_df['Predicted Temp'] = future_predictions

    # 显示未来7天的预测结果
    print(future_df[['Date', 'Predicted Temp']])

    # 绘制预测气温曲线
    plt.figure(figsize=(10, 6))
    plt.plot(future_df['Date'], future_df['Predicted Temp'], 'bo-', label='Predicted Temperature (Next 7 Days)')
    plt.title('Predicted Temperature for the Next 7 Days', fontsize=15)
    plt.xlabel('Date', fontsize=12)
    plt.ylabel('Temperature (℃)', fontsize=12)
    plt.xticks(rotation=45)
    plt.grid(True)
    plt.legend()
    # plt.show()


    # 返回预测结果
    return future_df[['Date', 'Predicted Temp']].to_dict(orient='records')


